/**
 * 查询条件生成工具
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import {distinct, FilterTypeEnum, toArray, toStringArray} from '@/soar';

const SENIOR_FILTER_ENUM = {
  logic: 'l', //  逻辑关系，即表示该条件与前面条件的逻辑关系（AND 或者 OR） 例 {logic: 'or', type: eq, field: id, value: '321456'} --> or id = 321456
  condition: 'c', //  条件 eq ne like 等
  value: 'v', //  条件 eq ne like 等
  and: 'a', //  嵌套与，即表示该数组里面的所有条件是与关系  （xxx and xxx and 。。。）
  or: 'o', //  嵌套或，即表示该数组里面的所有条件是或关系 （xxx or xxx or 。。。）
};

const PARAMS_ENUM = {
  filter: 'f',
  fields: 'fields',
  orderBys: 'orderBy',
  pageNumber: 'pageNo',
  pageSize: 'pageSize',
  queryType: 'queryType',
};

const VJOIN = '~';
const KJOIN = '-';

function getRule() {
  const rules = [
    `(eq|ne|lt|le|ge|gt|like|likeRight|likeLeft|notLike)${VJOIN}.+`,
    `(in|notIn)(${VJOIN}[^${VJOIN}]+)*`,
    `(between|notBetween)(${VJOIN}[^${VJOIN}]+){2}`,
    `isNull|isNotNull`,
  ];
  return new RegExp(`^[^${KJOIN}]+${KJOIN}((${rules.join(')|(')}))$`);
}
/**
 * 格式规则验证
 * @type {RegExp}
 */
const REGEXP = getRule();

/**
 * 生成查询参数
 * @param {Object} soarQuery
 * filters参数说明：
 *    例1：filters = [ 'username|nickname-like~admin', 'id(1,2)', 'id{1,2}, 'id<1', 'id**admin' ]
 */
export default function queryGenerator(soarQuery: { [p: string]: any }): object {
  if (typeof soarQuery !== 'object') {
    return {};
  }
  soarQuery = { ...soarQuery };
  const result: { [p: string]: any } = {};
  // 所需字段
  if (soarQuery.fields?.length) {
    result[PARAMS_ENUM.fields] = distinct(toStringArray(soarQuery.fields)).join(',');
    delete soarQuery.fields;
  }
  // 排序
  if (soarQuery.orderBys?.length) {
    result[PARAMS_ENUM.orderBys] = distinct(toStringArray(soarQuery.orderBys)).join(',');
    delete soarQuery.orderBys;
  }
  // 过滤器
  const filters = toArray(soarQuery.filters || []).concat();
  delete soarQuery.filters;
  // 处理过滤器
  for (const key in soarQuery) {
    const value = soarQuery[key];
    if (value || value === false || value === 0) {
      if (key.indexOf('-') !== -1) {
        filters.push(`${key}~${Array.isArray(value) ? value.join(VJOIN) : value}`);
      } else {
        result[key] = value;
      }
    }
  }
  result[PARAMS_ENUM.filter] = handleFilter(filters);
  // 返回结果
  return result;
}

/**
 * 继续快速过滤条件
 * @param filters
 */
function handleFilter(filters: string[]): string[] {
  for (let i = 0; i < filters.length; i++) {
    // 解析配置
    let filter = handleStringFilter(filters[i]);
    let index = filter.indexOf(KJOIN);
    const field = filter.substring(0, index);
    const temp = filter.substring(index + 1);
    index = temp.indexOf(VJOIN);
    const type = temp.substring(0, index);
    // 查询优化处理
    if (type === FilterTypeEnum.BETWEEN) {
      // 对单参数的 BETWEEN 转为大小于
      const value = temp.substring(index + 1).split(VJOIN);
      if (!value[0]) {
        filter = field + KJOIN + FilterTypeEnum.LT + VJOIN + value[1]; // 做小于处理
      } else if (!value[1]) {
        filter = field + KJOIN + FilterTypeEnum.GE + VJOIN + value[0]; // 做大于处理
      }
    } else if (type === FilterTypeEnum.IN) {
      // 对但参数的 IN 转为 等于
      const value = temp.substring(index + 1);
      if (!value.includes(VJOIN)) {
        filter = field + KJOIN + FilterTypeEnum.EQ + VJOIN + value; // 做等于处理
      }
    }
    if (!REGEXP.test(filter)) {
      throw new Error(`过滤条件格式错误：${filter}`);
    }
    filters[i] = filter;
  }
  return filters;
}

/**
 * 处理字符串格式
 * <= 转为 le；< 转为 lt；>= 转为 ge；> 转为 gt；
 * ** 转为 like；*= 转为 likeLeft；=* 转为 likeRight；!* 转为 notLike；
 * != 转为 ne；= 转为 eq；
 * !(v1,v2,v3...,vn) 转为 notIn；(v1,v2,v3...,vn) 转为 in；
 * !{v1,v2} 转为 notBetween；{v1,v2} 转为 between；
 * 例1：age{18,48} => age between 18 and 48
 * 例2：id(1,3,8,9) => id in(1,3,8,9)
 * 例3：name**admin => name like admin
 * 例4：code eq admin => code = admin
 * @param filter
 * @returns {string|*}
 */
function handleStringFilter(filter: string): string {
  filter = filter.trim();
  // 小于
  if (filter.includes('<=')) {
    return format(filter.split('<='), 'le');
  }
  if (filter.includes('<')) {
    return format(filter.split('<'), 'lt');
  }
  // 大于
  if (filter.includes('>=')) {
    return format(filter.split('>='), 'ge');
  }
  if (filter.includes('>')) {
    return format(filter.split('>'), 'gt');
  }
  // 模糊
  if (filter.includes('**')) {
    return format(filter.split('**'), 'like');
  }
  if (filter.includes('*=')) {
    return format(filter.split('*='), 'likeLeft');
  }
  if (filter.includes('=*')) {
    return format(filter.split('=*'), 'likeRight');
  }
  if (filter.includes('!*')) {
    return format(filter.split('!*'), 'notLike');
  }
  // 等于
  if (filter.includes('!=')) {
    return format(filter.split('!='), 'ne');
  }
  if (filter.includes('=')) {
    return format(filter.split('='), 'eq');
  }
  // 包含
  if (filter.includes('!(')) {
    const sIndex = filter.indexOf('!(');
    const eIndex = filter.lastIndexOf(')');
    const field = filter.substring(0, sIndex);
    const values = filter.substring(sIndex + 1, eIndex).replaceAll(',', VJOIN);
    return format([field, values], 'notIn');
  }
  if (filter.includes('(')) {
    const sIndex = filter.indexOf('(');
    const eIndex = filter.lastIndexOf(')');
    const field = filter.substring(0, sIndex);
    const values = filter.substring(sIndex + 1, eIndex).replaceAll(',', VJOIN);
    return format([field, values], 'in');
  }
  // 范围
  if (filter.includes('!{')) {
    const sIndex = filter.indexOf('!{');
    const eIndex = filter.lastIndexOf('}');
    const field = filter.substring(0, sIndex);
    const values = filter.substring(sIndex + 1, eIndex).replace(',', VJOIN);
    return format([field, values], 'notBetween');
  }
  if (filter.includes('{')) {
    const sIndex = filter.indexOf('{');
    const eIndex = filter.lastIndexOf('}');
    const field = filter.substring(0, sIndex);
    const values = filter.substring(sIndex + 1, eIndex).replace(',', VJOIN);
    return format([field, values], 'between');
  }
  return filter;
}

function format(ps: string[], type: string): string {
  return `${ps[0].trim()}${KJOIN}${type}${VJOIN}${ps[1].trim()}`;
}
